/**
 *	Copyright (c) 2009, SpLib.net
 * 	All rights reserved.
  	
 * 	Author:6spring
 * 	About:资源swf包管理
*/

package SpLib.display
{
	import SpLib.Com.Com;
	import SpLib.Com.SingletonFactory;
	import SpLib.Com.SpLibEvent;
	import SpLib.Net.NetLoaderQue;
	import SpLib.std.iterator;
	import SpLib.std.map;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.LoaderInfo;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.media.Sound;
	import flash.utils.Timer;

	public class ResMgr extends EventDispatcher
	{
		public static const RES_TYPE_BITMAP:String = "bitmap";
		public static const RES_TYPE_SOUND:String = "sound";
		
		public static const RES_OUT_TIME:uint =	3*60*1000;
		private static var s_Instance:ResMgr = null;
		
		private var m_ResPak:SpLib.std.map = null;
		private var m_szResCache:SpLib.std.map = null;
		private var m_DonwLoader:SpLib.Net.NetLoaderQue = null;
		private var m_ChkTime:Timer;
		
		public static function GetInstance():ResMgr
		{
			var gname:String = "SpLib.display.ResMgr";
			if(s_Instance==null)
			{
				s_Instance = SingletonFactory.Get(gname) as SpLib.display.ResMgr;
				if(s_Instance==null)
				{			
					s_Instance = new ResMgr();
					SingletonFactory.Reg(gname,s_Instance);
				}
			}			
	
			return s_Instance;
		}
		
		public function ResMgr()
		{
			if(s_Instance!=null)
			{
				throw new Error("ResMgr is Singleton");
			}
			
			if(SingletonFactory.Get("SpLib.display.ResMgr")!=null)
			{
				throw new Error("ResMgr is Singleton");
			}
			
			m_ResPak = new SpLib.std.map();
			m_szResCache = new SpLib.std.map(SpLib.Com.Com.CompareStr);
			
			m_DonwLoader = new SpLib.Net.NetLoaderQue();
			m_DonwLoader.addEventListener(SpLib.Net.NetLoaderQue.EVE_DOWNOK,OnDownPakOk);
			m_DonwLoader.addEventListener(SpLib.Net.NetLoaderQue.EVE_PROGRESS,OnDownPakProgress);
			m_DonwLoader.addEventListener(SpLib.Net.NetLoaderQue.EVE_COMPLETE,OnDownPakListComplete);
			
			m_ChkTime = new Timer(RES_OUT_TIME);
			m_ChkTime.addEventListener(TimerEvent.TIMER,ChkAct);
			m_ChkTime.start();
		}
		
		private function ChkAct(evt:TimerEvent):void
		{
			var iter:SpLib.std.iterator = m_ResPak.begin();
			while(iter!=m_ResPak.end())
			{
				var pak:ResPak = iter.m_Data as ResPak;
				pak.ChkAct();
				
				iter = iter.next;
			}
			
			//清除缓存图片
			var droplist:Array = new Array();
			iter = m_szResCache.begin();
			var skey:String;
			if(iter!=m_szResCache.end())
			{
				var res:ResNode = iter.m_Data as ResNode;
				if(res.ref==1)
				{
					skey = iter.m_Key;
					droplist.push(skey);
				}

				iter = iter.next;
			}
			
			var dropkey:String;
			for each(dropkey in droplist)
			{
				m_szResCache.remove(dropkey);
			}
		}
		
		public function GetCacheRes(reskey:String,type:String):*
		{
			var iter:SpLib.std.iterator = m_szResCache.find(reskey);
			if(iter!=m_szResCache.end())
			{
				var res:ResNode = iter.m_Data as ResNode;
				if(res.type==type)
				{
					res.AddRef();
					return res.data;
				}
				
				return null;
			}
			
			return null;
		}
		
		public function AddCacheRes(reskey:String,type:String,res:*):void
		{
			m_szResCache.setvalue(reskey,new ResNode(type,res));
		}
		
		public function ReleaseCacheRes(reskey:String):void
		{
			var iter:SpLib.std.iterator = m_szResCache.find(reskey);
			if(iter!=m_szResCache.end())
			{
				var res:ResNode = iter.m_Data as ResNode;
				res.Release();
			}
		}
		
		public function RemoveCacheRes(reskey:String):void
		{
			m_szResCache.remove(reskey);
		}
		
		public function GetBitmapData(pakname:String,name:String):BitmapData
		{
			var iter:SpLib.std.iterator = m_ResPak.find(pakname);
			if(iter==null) return null;
			var pak:ResPak = iter.m_Data as ResPak;
			return pak.GetBitmapData(name);
		}
		
		public function GetBitmapDataHFlip(pakname:String,name:String):BitmapData
		{
			var iter:SpLib.std.iterator = m_ResPak.find(pakname);
			if(iter==null) return null;
			var pak:ResPak = iter.m_Data as ResPak;
			return pak.GetBitmapDataHFlip(name);
		}
		
		public function GetBitmapDataByID(pakname:String,id:uint):BitmapData
		{
			var iter:SpLib.std.iterator = m_ResPak.find(pakname);
			if(iter==null) return null;
			var pak:ResPak = iter.m_Data as ResPak;
			return pak.GetBitmapDataByID(id);
		}
		
		public function GetBitmap(pakname:String,name:String):Bitmap
		{
			var imgdata:BitmapData = GetBitmapData(pakname,name);
			if(imgdata==null) return null;
			return new Bitmap(imgdata);
		}
		
		public function GetSound(pakname:String,name:String):Sound
		{	
			var iter:SpLib.std.iterator = m_ResPak.find(pakname);
			if(iter==null) return null;
			var pak:ResPak = iter.m_Data as ResPak;
			return pak.GetSound(name);
		}
		
		public function ListRes(pakname:String):Array
		{
			var iter:SpLib.std.iterator = m_ResPak.find(pakname);
			if(iter==null) return null;
			var pak:ResPak = iter.m_Data as ResPak;
			return pak.ListRes();
		}
	
		public function DownResPakList(filelist:Array):void
		{
			var i:int;
			var name:String;
			var downpath:String;
			var downlist:Array = new Array;
			for(i=0;i<filelist.length;i++)
			{
				name = filelist[i][0];
				downpath = filelist[i][1];
				
				if(AddResPak(name,downpath))
				{
					downlist.push(downpath);
				}
			}
				
			m_DonwLoader.AddDown(downlist);
			m_DonwLoader.Start();
		}
		
		private function AddResPak(name:String,url:String):Boolean
		{
			var iter:SpLib.std.iterator = m_ResPak.find(name);
			if(iter!=null) return false;
			
			var pak:ResPak = new ResPak(name,url);
			m_ResPak.insert(name,pak);
			return true;
		}
		
		private function FindPakByUrl(url:String):ResPak
		{
			var iter:SpLib.std.iterator = m_ResPak.begin();
			while(iter!=m_ResPak.end())
			{
				var pak:ResPak = iter.m_Data as ResPak;
				if(pak.downpath==url)
				{
					return pak;
				}
				
				iter = iter.next;
			}
			
			return null;
		}
		
		private function OnDownPakOk(evt:SpLibEvent):void
		{
			var filepath:String = evt.m_Agv[0] as String;
			var loadinfo:flash.display.LoaderInfo = evt.m_Agv[1] as flash.display.LoaderInfo;
			
			var iter:SpLib.std.iterator = m_ResPak.begin();
			while(iter!=m_ResPak.end())
			{
				var pak:ResPak = iter.m_Data as ResPak;
				if(pak.downpath==filepath)
				{
					if(!pak.LoadOk(loadinfo))
					{
						m_ResPak.erase(iter);
					}
					return;
				}
				
				iter = iter.next;
			}
		}
		
		private function OnStartDown(evt:SpLibEvent):void
		{
			var downpath:String = evt.m_Agv[0] as String;
			var pak:ResPak = FindPakByUrl(downpath);
			if(pak!=null)
			{
				this.dispatchEvent(new SpLibEvent(SpLib.Net.NetLoaderQue.EVE_STARTDOWN,evt.evename,[pak.name]));
			}	
		}
		
		private function OnDownPakProgress(evt:SpLibEvent):void
		{
			var downpath:String = evt.m_Agv[0] as String;
			var bytesLoaded:uint = evt.m_Agv[1] as uint;
			var bytesTotal:uint = evt.m_Agv[2] as uint;
			var pak:ResPak = FindPakByUrl(downpath);
			if(pak!=null)
			{
				this.dispatchEvent(new SpLibEvent(SpLib.Net.NetLoaderQue.EVE_PROGRESS,evt.evename,[pak.name,bytesLoaded,bytesTotal]));
			}
		}
		
		private function OnDownPakListComplete(evt:SpLibEvent):void
		{
			this.dispatchEvent(new SpLibEvent(SpLib.Net.NetLoaderQue.EVE_COMPLETE,evt.evename));
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////
import flash.utils.getTimer;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.media.Sound;
import SpLib.display.ResMgr;
import SpLib.Tools.Tr;
import SpLib.display.Graphics2D;

////////////////////////////////////////////////////////////////////////////////////////
class ResNode
{
	private var m_sType:String = "";
	private var m_Data:* = null;
	private var m_ActTime:uint = 0;
	private var m_lRef:int = 0;
	
	public function ResNode(type:String,data:*)
	{
		m_lRef = 1;
		m_sType = type;
		m_Data = data;
		m_ActTime = getTimer();
	}
	
	public function AddRef():void
	{
		++m_lRef;
	}
	
	public function Release():void
	{
		--m_lRef;
	}	
	
	public function get ref():int
	{
		return m_lRef;
	}
	
	public function get type():String
	{
		m_ActTime = getTimer();
		return m_sType;
	}
	
	public function get data():*
	{
		m_ActTime = getTimer();
		return m_Data;
	}
	
	public function Clear():void
	{
		m_Data = null;
		m_ActTime = 0;
	}
	
	public function Act():void
	{
		m_ActTime = getTimer();
	}
	
	public function ChkAct():Boolean
	{
		var now:uint = getTimer();
		return (now-m_ActTime)<SpLib.display.ResMgr.RES_OUT_TIME;
	}
}

////////////////////////////////////////////////////////////////////////////////////////
class ResPak
{
	private var m_sName:String = "";
	private var m_sClassName:String = "";
	private var m_sDownPath:String = "";
	private var m_pObj:Object = null;
	private var m_szImgSrc:SpLib.std.map = new SpLib.std.map();
	private var m_szImgSrcHFlip:SpLib.std.map = new SpLib.std.map();
	
	public function ResPak(name:String,downpath:String)
	{
		m_sName = name;
		m_sDownPath = downpath;
		
		var pidx:int = downpath.lastIndexOf(".");
		var didx:int = downpath.lastIndexOf("/");
		if(didx<0)
		{
			didx = downpath.lastIndexOf("\\");
		}
		
		var classname:String = downpath.substr(didx+1,pidx-didx-1);
		m_sClassName = "splib.res." + classname;
		
	}
		
	public function get name():String
	{
		return m_sName;
	}
		
	public function get downpath():String
	{
		return m_sDownPath;
	}
		
	public function LoadOk(loaderinfo:flash.display.LoaderInfo):Boolean
	{
		try
		{
			var resClass:Class = loaderinfo.applicationDomain.getDefinition(m_sClassName) as Class;
			if(resClass==null)
			{
				return false;
			}
		
			m_pObj = new resClass();
			SpLib.Tools.Tr.SysNote("加载资源包 "+m_sDownPath+"-->"+ m_sName +" 成功.");
		}
		catch(e:Error)
		{
			return false;
		}
		
		return true;
	}
	
	public function ChkAct():void
	{
		var datanode:ResNode;
		var idel:Array = new Array;
		var imgname:String;
		
		//释放图像
		var iter:SpLib.std.iterator = m_szImgSrc.begin();
		while(iter!=m_szImgSrc.end())
		{
			datanode = iter.m_Data as ResNode;
			if(!datanode.ChkAct())
			{
				imgname = iter.m_Key as String;
				idel.push(imgname);
				SpLib.Tools.Tr.Info("释放资源 < "  + m_sName + "::" + imgname + ">");
			}
			
			iter = iter.next;
		}
		
		var key:String;
		for each(key in idel)
		{
			m_szImgSrc.remove(key);
		}
		
		//释放水平翻转图像
		iter = m_szImgSrcHFlip.begin();
		while(iter!=m_szImgSrcHFlip.end())
		{
			datanode = iter.m_Data as ResNode;
			if(!datanode.ChkAct())
			{
				imgname = iter.m_Key as String;
				idel.push(imgname);
				SpLib.Tools.Tr.Info("释放资源 < "  + m_sName + "::" + imgname + ">");
			}
			
			iter = iter.next;
		}
		
		for each(key in idel)
		{
			m_szImgSrcHFlip.remove(key);
		}
	}
	
	private function GetResInfo(name:String,wtype:String):Array
	{
		if(m_pObj==null)
		{
			return null;
		}
		
		var resinfo:Array = m_pObj.GetRes(name) as Array;
		if(resinfo==null || resinfo.length!=3)
		{
			return null;
		}
		
		var type:String = resinfo[1] as String;
		if(type!=wtype)
		{
			return null;
		}
		
		return resinfo;
	}
	
	private function GetResInfoByID(id:uint,wtype:String):Array
	{
		if(m_pObj==null)
		{
			return null;
		}
		
		var resinfo:Array = m_pObj.GetResByID(id) as Array;
		if(resinfo==null || resinfo.length!=4)
		{
			return null;
		}
		
		var resClass:Class = resinfo[0] as Class;
		if(resClass==null)
		{
			return null;
		}
		
		var type:String = resinfo[2] as String;
		if(type!=wtype)
		{
			return null;
		}
		
		return resinfo;
	}
	
	private function LoadBitmapData(name:String):BitmapData
	{		
		var imginfo:Array = GetResInfo(name,SpLib.display.ResMgr.RES_TYPE_BITMAP) as Array;
		if(imginfo==null)
		{
			return null;
		}
			
		var imgClass:Class = imginfo[0] as Class;
	
		var size:Array = imginfo[2] as Array;
		if(size==null || size.length!=2)
		{
			return null;
		}
			
		var data:BitmapData = new imgClass(size[0] as int,size[1] as int) as BitmapData;
		m_szImgSrc.insert(name,new ResNode(SpLib.display.ResMgr.RES_TYPE_BITMAP,data));
		return data;
	}
	
	private function LoadBitmapDataByID(id:uint):BitmapData
	{			
		var imginfo:Array = GetResInfoByID(id,SpLib.display.ResMgr.RES_TYPE_BITMAP) as Array;
		if(imginfo==null)
		{
			return null;
		}
			
		var imgClass:Class = imginfo[0] as Class;
		var name:String = imginfo[1] as String;
		
		var size:Array = imginfo[3] as Array;
		if(size==null || size.length!=2)
		{
			return null;
		}
			
		var data:BitmapData = new imgClass(size[0] as int,size[1] as int) as BitmapData;
		m_szImgSrc.insert(name,new ResNode(SpLib.display.ResMgr.RES_TYPE_BITMAP,data));
		return data;
	}
	
	private function LoadSound(name:String):Sound
	{	
		var sndinfo:Array = GetResInfo(name,SpLib.display.ResMgr.RES_TYPE_BITMAP) as Array;
		if(sndinfo==null)
		{
			return null;
		}
			
		var sndClass:Class = sndinfo[0] as Class;

		var data:Sound = new sndClass() as Sound;
		m_szImgSrc.insert(name,new ResNode(SpLib.display.ResMgr.RES_TYPE_BITMAP,data));
		return data;
	}
	
	public function ListRes():Array
	{
		if(m_pObj==null)
		{
			return null;
		}
		
		return m_pObj.ListRes() as Array;
	}
		
	public function GetBitmapData(name:String):BitmapData
	{	
		var iter:SpLib.std.iterator = m_szImgSrc.find(name);
		if(iter!=m_szImgSrc.end())
		{
			var datanode:ResNode = iter.m_Data as ResNode;
			if(datanode.type!=SpLib.display.ResMgr.RES_TYPE_BITMAP)
			{
				return null;
			}
			
			datanode.Act();
			return datanode.data as BitmapData;
		}
			
		return LoadBitmapData(name);
	}
	
	public function GetBitmapDataHFlip(name:String):BitmapData
	{		
		var iter:SpLib.std.iterator = m_szImgSrcHFlip.find(name);
		if(iter!=m_szImgSrc.end())
		{
			var datanode:ResNode = iter.m_Data as ResNode;
			datanode.Act();
			return datanode.data as BitmapData;
		}
		
		var img:BitmapData = GetBitmapData(name);
		if(img==null)
		{
			return null;
		}
			
		var dc:SpLib.display.Graphics2D = new SpLib.display.Graphics2D();
		dc.Create(img.width,img.height);
		dc.Draw_HFlip(img);
		m_szImgSrcHFlip.insert(name,new ResNode(SpLib.display.ResMgr.RES_TYPE_BITMAP,dc.data));
		
		return dc.data;
	}
	
	public function GetBitmapDataByID(id:uint):BitmapData
	{
		var iter:SpLib.std.iterator = m_szImgSrc.find(id);
		if(iter!=m_szImgSrc.end())
		{
			var datanode:ResNode = iter.m_Data as ResNode;
			if(datanode.type!=SpLib.display.ResMgr.RES_TYPE_BITMAP)
			{
				return null;
			}
			
			datanode.Act();
			return datanode.data as BitmapData;
		}
			
		return LoadBitmapDataByID(id);
	}
	
	public function GetSound(name:String):Sound
	{		
		var iter:SpLib.std.iterator = m_szImgSrc.find(name);
		if(iter!=m_szImgSrc.end())
		{
			var datanode:ResNode = iter.m_Data as ResNode;
			
			if(datanode.type!=SpLib.display.ResMgr.RES_TYPE_SOUND)
			{
				return null;
			}
			
			datanode.Act();
			return datanode.data as Sound;
		}
			
		return LoadSound(name);
	}
}